import importlib
import os
from flask import Blueprint, Flask, jsonify, request
from flask_migrate import Migrate

from config import getConfig

from packages.core.storage.file_upload_service import FileUploadService
from packages.common.base import R
from packages.tools.load_enum import LoadEnum
from packages.core.auth.decorators import HasPerm
from packages.core.auth.login_user_holder import LoginUserHolder
from packages.core.auth.token import TokenStrategyFactory, redis_client
from packages.core.exception import AssertTool, ExceptionConfig, GlobalErrorEnum
from packages.core.logging_config import LoggingConfig
from packages.core.request_log import RequestLog
from packages.common.base_model import db
from modules.sys.services import LowCodeServiceUtil
from modules.sys.services.dict_service import DictService
from modules.sys.services.user_service import UserService
from packages.engine.flask_config import FlowEngineConfig
from datetime import datetime
from werkzeug.middleware.proxy_fix import ProxyFix
from flask.json.provider import DefaultJSONProvider

from packages.tools.base_tool import ant_matcher, generate_id
from packages.common.base import BasePageForm

app = Flask(__name__)
# 应用 ProxyFix 中间件
# x_for=1 表示信任代理服务器的 X-Forwarded-For 头中的第一个 IP 地址
app.wsgi_app = ProxyFix(app.wsgi_app, x_for=1)

# 获取controllers目录下所有的.py文件路径
# 修改为扫描 modules 目录及其子目录
controllers_dir = "modules"
controller_files = []
for root, dirs, files in os.walk(controllers_dir):
    for file in files:
        if file.endswith("_controller.py") and not file.startswith("__"):
            controller_files.append(os.path.join(root, file))

# 动态导入每个控制器模块并注册蓝图
for file_path in controller_files:
    module_name = os.path.splitext(os.path.basename(file_path))[0]  # 去掉'.py'后缀
    # 根据文件路径生成蓝图名称，例如 sys/controllers/auth_controller.py -> auth
    blueprint_name = module_name.replace("_controller", "")
    # 构建模块的完整名称，例如 modules.sys.controllers.auth_controller
    full_module_name = file_path.replace(os.sep, ".").replace(".py", "")
    spec = importlib.util.spec_from_file_location(full_module_name, file_path)
    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)

    # 检查模块是否有对应的blueprint属性，如果有，则注册它
    if hasattr(module, blueprint_name) and isinstance(
        getattr(module, blueprint_name), Blueprint
    ):
        app.register_blueprint(getattr(module, blueprint_name))

# 从配置对象中加载
app.config.from_object(getConfig())

# 初始化db
with app.app_context():
    db.init_app(app)
# 初始化redis
redis_client.init_app(app)
app.before_request(LoginUserHolder.before_request)


# 自定义日期转换器，格式化日期
class CustomJSONProvider(DefaultJSONProvider):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.strftime('%Y-%m-%d %H:%M:%S')  # 统一格式
        return super().default(obj)


app.json = CustomJSONProvider(app)
# 配置字典
LoadEnum(app)
# 配置日志
LoggingConfig(app)
# 配置请求日志
RequestLog(app)
# 配置异常处理
ExceptionConfig(app)
# 配置上传存储服务
FileUploadService(app)


# 配置工作流引擎----start
def check_permission(access, mode=None):
    if not TokenStrategyFactory.check_permission_only(access, mode=mode):
        AssertTool.raise_biz(GlobalErrorEnum.GL99990406)


dictService = DictService(db=db)
userService = UserService(db=db)


def dict_api(dictType):
    return dictService.get_by_dict_type(dictType)


def user_api(userId):
    if isinstance(userId, dict):
        return userService.page(BasePageForm(data=userId))
    return userService.get_wf_user_by_id(userId=userId)


FlowEngineConfig(
    app,
    db=db.session,
    func_generate_id=generate_id,
    func_get_current_user_id=LoginUserHolder.get_user_id,
    func_check_permission=check_permission,
    func_dict_api=dict_api,
    func_user_api=user_api
)
# 配置工作流引擎----end
# 执行数据库迁移相关操作。flask db指令不能直接使用，需要获取Migrate实例，这里注册Migrate实例
migrate = Migrate(app, db)
# 配置代码生成器-仅开发环境配置
if app.config.get("ENV") == "dev":
    from generator import CodeGenerator

    CodeGenerator(app, db)


# 低代码-通用下拉接口
@app.route("/<moduleName>/<tableName>/select", methods=["POST"])
@HasPerm(name="通过下拉")
def lowcode_select(moduleName, tableName):
    return R.data(LowCodeServiceUtil.select(moduleName=moduleName, tableName=tableName))


# 拦截器-demo环境拦截
if app.config.get("ENV") == "demo":

    @app.before_request
    def check_blacklist():
        path = request.path
        BLACK_LIST = app.config.get("BLACK_LIST")
        if any(ant_matcher(path, blacklisted_path) for blacklisted_path in BLACK_LIST):
            response = jsonify(
                {
                    "code": 99990406,
                    "msg": "您没有资源访问权限，请联系管理员！",
                    "data": None,
                }
            )
            return response


if __name__ == "__main__":
    app.run(
        host="0.0.0.0",
        port=app.config.get("PORT", 5000),
        debug=app.config.get("DEBUG", True),
    )
